<?php
namespace Illuminate\Session\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Session\SessionManager;
use Illuminate\Session\CookieSessionHandler;
use Illuminate\Contracts\Session\Session;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Response;

class StartSession {
    protected $manager;
    protected $sessionHandled = false;

    public function __construct(SessionManager $manager) {
        $this->manager = $manager;
    }

    public function handle($request, Closure $next) { // ok
        $this->sessionHandled = true;

        if ( $this->sessionConfigured() ) {
            $request->setLaravelSession(
                $session = $this->startSession($request)
            );

            $this->collectGarbage($session); // 垃圾回收
        }

        $response = $next($request);

        if ( $this->sessionConfigured() ) {
            $this->storeCurrentUrl($request, $session); // 存储当前 url

            $this->addCookieToResponse($response, $session); // 附加 cookie
        }

        return $response;
    }
    public function terminate($request, $response) {
        if ($this->sessionHandled && $this->sessionConfigured() && ! $this->usingCookieSessions()) {
            $this->manager->driver()->save();
        }
    }
    protected function startSession(Request $request) {
        return tap($this->getSession($request), function ($session) use ($request) {
            $session->setRequestOnHandler($request);

            $session->start();
        });
    }
    public function getSession(Request $request) {
        return tap($this->manager->driver(), function ($session) use ($request) {
            $session->setId($request->cookies->get($session->getName()));
        });
    }

    protected function collectGarbage(Session $session) { // 垃圾回收
        $config = $this->manager->getSessionConfig();

        if ( $this->configHitsLottery($config) ) {
            $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
        }
    }
    protected function configHitsLottery(array $config) { // 随机回收 方法 [1,100] <=2 0.02
        return random_int(1, $config['lottery'][1]) <= $config['lottery'][0];
    }
    protected function storeCurrentUrl(Request $request, $session) {
        if ( $request->method() === 'GET' && $request->route() && ! $request->ajax() ) {
            $session->setPreviousUrl($request->fullUrl());
        }
    }
    protected function addCookieToResponse(Response $response, Session $session) { // 附加 Cookie 到 响应
        if ( $this->usingCookieSessions() ) $this->manager->driver()->save();

        if ( $this->sessionIsPersistent($config = $this->manager->getSessionConfig()) ) {
            $response->headers->setCookie(new Cookie(
                $session->getName(), $session->getId(), $this->getCookieExpirationDate(),
                $config['path'], $config['domain'], $config['secure'] ?? false,
                $config['http_only'] ?? true, false, $config['same_site'] ?? null
            ));
        }
    }
    protected function getSessionLifetimeInSeconds() { // 生存时间
        return ($this->manager->getSessionConfig()['lifetime'] ?? null) * 60;
    }
    protected function getCookieExpirationDate() { // cookie 过期时间
        $config = $this->manager->getSessionConfig();

        return $config['expire_on_close'] ? 0 : Carbon::now()->addMinutes($config['lifetime']);
    }
    protected function sessionConfigured() { // 已配置
        return ! is_null($this->manager->getSessionConfig()['driver'] ?? null);
    }
    protected function sessionIsPersistent(array $config = null) { // 持久化
        $config = $config ?: $this->manager->getSessionConfig();

        return ! in_array($config['driver'], [null, 'array']);
    }
    protected function usingCookieSessions() { // 基于 Cookie 的 Session
        if ( $this->sessionConfigured() ) {
            return $this->manager->driver()->getHandler() instanceof CookieSessionHandler;
        }

        return false;
    }
}
